home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Unix / top-v0_3 / Source / machine / m_next32.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-15  |  27.8 KB  |  1,023 lines

  1. /*
  2.  * top - a top users display for Unix
  3.  * NEXTSTEP v.0.3  2/14/1996 tpugh
  4.  *
  5.  * SYNOPSIS:  any m68k or intel NEXTSTEP v3.x system
  6.  *
  7.  * DESCRIPTION:
  8.  *    This is the machine-dependent module for NEXTSTEP v3.x
  9.  *    Reported to work for NEXTSTEP v3.0, v3.2, and v3.3 Mach OS:
  10.  *        NEXTSTEP v3.0 on Motorola machines.
  11.  *        NEXTSTEP v3.2 on Intel and Motorola machines.
  12.  *        NEXTSTEP v3.3 on Intel and Motorola machines.
  13.  *    Problem with command column for (Choose next40 for fix):
  14.  *        NEXTSTEP v3.2 on HP machines.
  15.  *        NEXTSTEP v3.3 on HP and Sparc machines.
  16.  *    Has not been tested for NEXTSTEP v2.x machines, although it should work.
  17.  *    Has not been tested for NEXTSTEP v3.1 machines, although it should work.
  18.  *    Install "top" with the permissions 4755.
  19.  *        tsunami# chmod 4755 top
  20.  *        tsunami# ls -lg top
  21.  *        -rwsr-xr-x  1 root     kmem      121408 Sep  1 10:14 top*
  22.  *    With the kmem group sticky bit set, we can read kernal memory without problems,
  23.  *    but to communicate with the Mach kernal for task and thread info, it requires
  24.  *    root privileges.
  25.  *
  26.  * LIBS: 
  27.  *
  28.  * Need the compiler flag, "-DSHOW_UTT", to see the user task and thread task
  29.  * data structures to report process info.
  30.  *
  31.  * CFLAGS: -DSHOW_UTT
  32.  *
  33.  *
  34.  * AUTHORS:        Tim Pugh <tpugh@oce.orst.edu>
  35.  */
  36.  
  37. #include <sys/types.h>
  38. #include <sys/signal.h>
  39. #include <sys/param.h>
  40.  
  41. #include <stdio.h>
  42. #include <nlist.h>
  43. #include <math.h>
  44. #include <sys/dir.h>
  45. #include <sys/user.h>
  46. #include <sys/proc.h>
  47. #include <sys/dk.h>
  48. #include <sys/vm.h>
  49. #include <sys/file.h>
  50. #include <sys/time.h>
  51. #import <mach/mach.h>
  52. #include <sys/vmmeter.h>
  53. #import <mach/vm_statistics.h>
  54.  
  55. #import "machine/m_next_task.h"
  56.  
  57. /*  Problems on NS/HPPA machines.  Also, not currently used by source code.
  58.  *#define DOSWAP
  59.  */
  60.  
  61. #include "top.h"
  62. #include "machine.h"
  63. #include "utils.h"
  64.  
  65. extern int errno, sys_nerr;
  66. extern char *sys_errlist[];
  67. #define strerror(e) (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error")
  68.  
  69. #define VMUNIX  "/mach"
  70. #define KMEM    "/dev/kmem"
  71. #define MEM     "/dev/mem"
  72. #ifdef DOSWAP
  73. #define SWAP    "/dev/drum"
  74. #endif
  75.  
  76. /* NeXT BSD process structure does not contain locations to hold info such as
  77.  * cpu usage, memory usage, resident core memory, or cpu time data.  So I've made
  78.  * a new process structure which composites the NeXT structure and the missing
  79.  * system info.
  80.  */
  81. struct proc_unix {
  82.     struct proc *p_self;        /* Each p_self points to a element in pbase. */
  83.     int p_pctcpu;                /* Scaled percent cpu usage. */
  84.     int p_vsize;                /* Total VM memory usage. */
  85.     int p_rsize;                /* Resident core memory usage. */
  86.     int p_cptime;                /* scaled CPU Time */
  87.     int run_state;                /* Task run state. */
  88.     int flags;                    /* Task state flags. */
  89.     int nthreads;                /* Number of threads per Task. */
  90.     int cur_priority;            /* Current main thread priority */
  91. };
  92.  
  93. /* Contains the list of processes. */
  94. struct handle
  95. {
  96.     struct proc_unix *list;        /* points to list of valid proc pointer */
  97.     int count;                    /* number of pointers */
  98.     int current;                /* Index of the current process formatting */
  99. };
  100.  
  101. /* declarations for load_avg */
  102. #include "loadavg.h"
  103.  
  104. /* define what weighted cpu is. */
  105. #define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
  106.              ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
  107.  
  108. /*  The following three variables are not defined in NeXT's process structure.
  109.  *    So they are zeroed until other ways of obtaining the info are found.
  110.  */
  111. /* what we consider to be process size: */
  112. /* #define PROCSIZE(pp)    ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize) */
  113. #define PROCSIZE(pp)    (0)
  114.  
  115. /* #define P_RSSIZE(pp)    ((pp)->p_rssize) */
  116. #define    P_RSSIZE(pp)    (0)
  117.  
  118. /* #define P_CPTICKS(pp)    ((pp)->p_cpticks) */
  119. #define P_CPTICKS(pp)    (0)
  120.  
  121.  
  122. extern int thread_stats(int p_pid, struct thread_basic_info *info, int *count);
  123. extern int mach_load_avg(void);
  124. extern kern_return_t task_stats(int p_pid, struct task_basic_info *info);
  125.  
  126. /* definitions for indices in the nlist array */
  127. #define X_AVENRUN    0
  128. #define X_CCPU        1
  129. #define X_NPROC        2
  130. #define X_PROC        3
  131. #define X_TOTAL        4
  132. #define X_CP_TIME    5
  133. #define X_MPID        6
  134. #define X_HZ        7
  135.  
  136. static struct nlist nlst[] = {
  137.     { "_avenrun" },        /* 0 */
  138.     { "_cpu_clk" },        /* 1 */
  139.     { "_max_proc" },    /* 2 */
  140.     { "_allproc" },        /* 3 */
  141.     { "_total" },        /* 4 */
  142.     { "_cp_time" },        /* 5 */
  143.     { "_mpid" },        /* 6 */
  144.     { "_hz" },            /* 7 */
  145.     { 0 }
  146. };
  147.  
  148. /*
  149.  *  These definitions control the format of the per-process area
  150.  */
  151.  
  152. static char header[] =
  153.   "  PID X        STATE PRI NICE  THR VSIZE RSIZE   %MEM   %CPU   TIME COMMAND";
  154. /* static char header[] =
  155.  * "  PID X        STATE PRI NICE  THR VSIZE RSIZE   %MEM  %WCPU   %CPU   TIME COMMAND"; 
  156.  */
  157.  
  158. /* 0123456   -- field to fill in starts at header+6 */
  159. #define UNAME_START 6
  160.  
  161. #define Proc_format \
  162.     "%5d %-8.8s %-5s %3d %4d %4d %5s %5s %6.2f %6.2f %6s %.14s"
  163. /* #define Proc_format \
  164.  *    "%5d %-8.8s %-5s %3d %4d %4d %5s %5s %6.2f %6.2f %6.2f %6s %.14s"
  165.  */
  166.  
  167.  
  168. /* process state names for the "STATE" column of the display */
  169. /* the extra nulls in the string "run" are for adding a slash and
  170.    the processor number when needed */
  171. char *state_abbrev[] =
  172. {
  173.     "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
  174. };
  175. char *mach_state[] =
  176. {
  177.     "", "R", "T", "S", "U", "H"
  178. };
  179. char *flags_state[] =
  180. {
  181.     "", "W", "I"
  182. };
  183.  
  184. /* these are for detailing the process states */
  185. int process_states[7];
  186. /* char *procstatenames[] = {
  187.  *    "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
  188.  *    " zombie, ", " stopped, ",
  189.  *    NULL
  190.  *};
  191.  */
  192. char *procstatenames[] = {
  193.     "", " running, ", " stopped, ", " sleeping, ", " uninterruptable, ",
  194.     " halted, ", " zombie ", NULL
  195. };
  196.  
  197.  
  198. static int kmem, mem;
  199. #ifdef DOSWAP
  200. static int swap;
  201. #endif
  202.  
  203. /* values that we stash away in _init and use in later routines */
  204.  
  205. static double logcpu;
  206.  
  207. /* these are retrieved from the kernel in _init */
  208.  
  209. static unsigned long proc;
  210. static          int  nproc;
  211. static          long hz;
  212. static load_avg  ccpu;
  213. static          int  ncpu = 0;
  214.  
  215. /* these are offsets obtained via nlist and used in the get_ functions */
  216.  
  217. static unsigned long avenrun_offset;
  218. static unsigned long mpid_offset;
  219. static unsigned long total_offset;
  220. static unsigned long cp_time_offset;
  221.  
  222. /* these are for calculating cpu state percentages */
  223.  
  224. static long cp_time[CPUSTATES];
  225. static long cp_old[CPUSTATES];
  226. static long cp_diff[CPUSTATES];
  227.  
  228. /* these are for detailing the cpu states */
  229.  
  230. int cpu_states[4];
  231. char *cpustatenames[] = {
  232.     "user", "nice", "system", "idle", NULL
  233. };
  234.  
  235. /* these are for detailing the memory statistics */
  236. int memory_stats[7];
  237. /* char *memorynames[] = {
  238.  *   "Real: ", "K/", "K act/tot  ", "Virtual: ", "K/",
  239.  *    "K act/tot  ", "Free: ", "K", NULL
  240.  * };
  241.  */
  242. char *memorynames[] = {
  243.     "K Tot, ", "K Act, ", "K Inact, ", "K Wired, ", "K Free, ", "K in, ", "K out ", NULL
  244. };
  245.  
  246. /* these are for keeping track of the proc array */
  247. static int bytes;
  248. static int pref_count;
  249. static struct proc *pbase;
  250. static struct proc_unix *pref;
  251.  
  252. /* these are for getting the memory statistics */
  253.  
  254. static int pageshift;        /* log base 2 of the pagesize */
  255.  
  256. /* define pagetok in terms of pageshift */
  257. #define pagetok(size) ((size) << pageshift)
  258.  
  259. /* useful externals */
  260. extern int errno;
  261. extern char *sys_errlist[];
  262.  
  263. long lseek();
  264. long time();
  265.  
  266. machine_init(struct statics *statics)
  267. {
  268.     register int i = 0;
  269.     register int pagesize;
  270.     
  271.     if ((kmem = open(KMEM, O_RDONLY)) == -1) {
  272.     perror(KMEM);
  273.     return(-1);
  274.     }
  275.     if ((mem = open(MEM, O_RDONLY)) == -1) {
  276.     perror(MEM);
  277.     return(-1);
  278.     }
  279.  
  280. #ifdef DOSWAP
  281.     if ((swap = open(SWAP, O_RDONLY)) == -1) {
  282.     perror(SWAP);
  283.     return(-1);
  284.     }
  285. #endif
  286.  
  287.     /* get the list of symbols we want to access in the kernel */
  288.     (void) nlist(VMUNIX, nlst);
  289.     if (nlst[0].n_type == 0)
  290.     {
  291.         fprintf(stderr, "top: nlist failed\n");
  292.         return(-1);
  293.     }
  294.  
  295.     /* make sure they were all found */
  296.     if (i > 0 && check_nlist(nlst) > 0)
  297.     {
  298.         return(-1);
  299.     }
  300.  
  301.     /* get the symbol values out of kmem */
  302.     (void) getkval(nlst[X_PROC].n_value, (int *)(&proc), sizeof(proc),
  303.                     nlst[X_PROC].n_un.n_name);
  304.     (void) getkval(nlst[X_NPROC].n_value, &nproc, sizeof(nproc),
  305.                     nlst[X_NPROC].n_un.n_name);
  306.     (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
  307.                     nlst[X_HZ].n_un.n_name);
  308.     (void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu), sizeof(ccpu),
  309.                     nlst[X_CCPU].n_un.n_name);
  310.  
  311.  
  312.     /* stash away certain offsets for later use */
  313.     mpid_offset = nlst[X_MPID].n_value;
  314.     avenrun_offset = nlst[X_AVENRUN].n_value;
  315.     total_offset = nlst[X_TOTAL].n_value;
  316.     cp_time_offset = nlst[X_CP_TIME].n_value;
  317.     
  318.  
  319.     /* this is used in calculating WCPU -- calculate it ahead of time */
  320.     ccpu = mach_load_avg();
  321.     logcpu = log((double)(ccpu)/LOAD_SCALE);
  322.  
  323.     /* allocate space for proc structure array and array of pointers */
  324.     bytes = nproc * sizeof(struct proc);
  325.     pbase = (struct proc *)malloc(bytes);
  326.     pref  = (struct proc_unix *)malloc((nproc+1) * sizeof(struct proc_unix *));
  327.  
  328.     /* Just in case ... */
  329.     if (pbase == (struct proc *)NULL || pref == (struct proc_unix *)NULL)
  330.     {
  331.     fprintf(stderr, "top: can't allocate sufficient memory\n");
  332.     return(-1);
  333.     }
  334.  
  335.     /* get the page size with "getpagesize" and calculate pageshift from it */
  336.     pagesize = getpagesize();
  337.     pageshift = ceil(log(pagesize)/log(2.0));
  338.  
  339.     /* we only need the amount of log(2)1024 for our conversion */
  340.     pageshift -= LOG1024;
  341.  
  342.     /* fill in the statics information */
  343.     statics->procstate_names = procstatenames;
  344.     statics->cpustate_names = cpustatenames;
  345.     statics->memory_names = memorynames;
  346.  
  347.     /* all done! */
  348.     return(0);
  349. }
  350.  
  351. char *format_header(register char *uname_field)
  352. {
  353.     register char *ptr;
  354.  
  355.     ptr = header + UNAME_START;
  356.     while (*uname_field != '\0')
  357.     {
  358.     *ptr++ = *uname_field++;
  359.     }
  360.  
  361.     return(header);
  362. }
  363.  
  364. static int swappgsin = -1;
  365. static int swappgsout = -1;
  366. static vm_statistics_data_t vm_stats;
  367. static host_basic_info_data_t  host_stats;
  368.  
  369. get_system_info(struct system_info *si)
  370. {
  371.     load_avg avenrun[3];
  372.     long total;
  373.  
  374.     /* get the cp_time array */
  375.     (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
  376.            "_cp_time");
  377.  
  378.     /* get load average array */
  379.     (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
  380.            "_avenrun");
  381.  
  382.     /* get mpid -- process id of last process */
  383.     (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
  384.            "_mpid");
  385.  
  386.     /* convert load averages to doubles */
  387.     {
  388.     register int i;
  389.     register double *infoloadp;
  390.     register load_avg *sysloadp;
  391.  
  392.     infoloadp = si->load_avg;
  393.     sysloadp = avenrun;
  394.     for (i = 0; i < 3; i++)
  395.     {
  396.         *infoloadp++ = loaddouble(*sysloadp++);
  397.     }
  398.     }
  399.  
  400.     /* convert cp_time counts to percentages */
  401.     total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
  402.  
  403.     /* sum memory statistics */
  404.     {
  405.         /* get total -- systemwide main memory usage structure */
  406.         /* Does not work on NeXT system.  Use vm_statistics() for paging info. */
  407.         /* struct vmtotal total;
  408.          * (void) getkval(total_offset, (int *)(&total), sizeof(total),
  409.          *           "_total");
  410.          */
  411.         /* convert memory stats to Kbytes */
  412.         /* memory_stats[0] = -1;
  413.          * memory_stats[1] = pagetok(total.t_arm);
  414.          * memory_stats[2] = pagetok(total.t_rm);
  415.          * memory_stats[3] = -1;
  416.          * memory_stats[4] = pagetok(total.t_avm);
  417.          * memory_stats[5] = pagetok(total.t_vm);
  418.          * memory_stats[6] = -1;
  419.          * memory_stats[7] = pagetok(total.t_free);
  420.          */
  421.         kern_return_t status;
  422.         unsigned int count=HOST_BASIC_INFO_COUNT;
  423.         status = vm_statistics(task_self(), &vm_stats);
  424.         if(status != KERN_SUCCESS)
  425.         {
  426.             mach_error("An error calling vm_statistics()!", status);
  427.         }
  428.         status = host_info(host_self(), HOST_BASIC_INFO, (host_info_t)&host_stats, &count);
  429.         if(status != KERN_SUCCESS)
  430.         {
  431.             mach_error("An error calling host_info()!", status);
  432.         }
  433.         /* convert memory stats to Kbytes */
  434.         memory_stats[0] = pagetok(host_stats.memory_size / vm_stats.pagesize);
  435.         memory_stats[1] = pagetok(vm_stats.active_count);
  436.         memory_stats[2] = pagetok(vm_stats.inactive_count);
  437.         memory_stats[3] = pagetok(vm_stats.wire_count);
  438.         memory_stats[4] = pagetok(vm_stats.free_count);
  439.         if (swappgsin < 0)
  440.         {
  441.             memory_stats[5] = 1;
  442.             memory_stats[6] = 1;
  443.         } else {
  444.             memory_stats[5] = pagetok(((vm_stats.pageins - swappgsin)));
  445.             memory_stats[6] = pagetok(((vm_stats.pageouts - swappgsout)));
  446.         }
  447.         swappgsin = vm_stats.pageins;
  448.         swappgsout = vm_stats.pageouts;
  449.     }
  450.  
  451.     /* set arrays and strings */
  452.     si->cpustates = cpu_states;
  453.     si->memory = memory_stats;
  454. }
  455.  
  456. static struct handle handle;
  457.  
  458. caddr_t get_process_info(struct system_info *si, 
  459.                          struct process_select *sel, 
  460.                          int (*compare)())
  461. {
  462.     int i, j;
  463.     int total_procs;
  464.     int active_procs;
  465.     struct proc *pp;
  466.     struct task_basic_info taskInfo;
  467.     struct thread_basic_info threadInfo;
  468.     kern_return_t thread_status;
  469.     kern_return_t task_status;
  470.     int threadCount;
  471.  
  472.     /* these are copied out of sel for speed */
  473.     int show_idle;
  474.     int show_system;
  475.     int show_uid;
  476.     int show_command;
  477.  
  478.     /* get a pointer to the states summary array */
  479.     si->procstates = process_states;
  480.  
  481.     /* set up flags which define what we are going to select */
  482.     show_idle = sel->idle;
  483.     show_system = sel->system;
  484.     show_uid = sel->uid != -1;
  485.     show_command = sel->command != NULL;
  486.  
  487.     (void) getkval(nlst[X_PROC].n_value, (int *)(&proc), sizeof(proc),
  488.                     nlst[X_PROC].n_un.n_name);
  489.  
  490.     /* count up process states and get pointers to interesting procs */
  491.     total_procs = 0;
  492.     active_procs = 0;
  493.     memset((char *)process_states, 0, sizeof(process_states));
  494.     i = 0;
  495.     j = 0;
  496.     do {
  497.         if(i == 0) {
  498.                /* read first proc structure */
  499.             (void) getkval(proc, (int *)&pbase[i], sizeof(struct proc), "first proc");
  500.          } else {
  501.             (void) getkval(pp->p_nxt, (int *)&pbase[i], sizeof(struct proc), "nxt proc");
  502.         }
  503.         pp = &pbase[i];
  504.  
  505.         thread_status = thread_stats(pp->p_pid, &threadInfo, &threadCount);
  506.         task_status = task_stats(pp->p_pid, &taskInfo);
  507.     /*
  508.      *  Process slots that are actually in use have a non-zero
  509.      *  status field.  Processes with SSYS set are system
  510.      *  processes---these get ignored unless show_sysprocs is set.
  511.      */
  512.         if (pp->p_stat != 0 &&
  513.             (show_system || ((pp->p_flag & SSYS) == 0)))
  514.         {
  515.             total_procs++;
  516. /* Using thread info for process states. */
  517. /*            process_states[pp->p_stat]++; */
  518.             if(thread_status==KERN_SUCCESS)
  519.                 process_states[threadInfo.run_state]++;
  520.             if ((pp->p_stat != SZOMB) &&
  521.                 (show_idle || (pp->p_stat == SRUN)) &&
  522.                 (!show_uid || pp->p_uid == (uid_t)sel->uid))
  523.             {
  524.                 pref[j].p_self = pp;
  525.                 if(thread_status==KERN_SUCCESS)
  526.                 {
  527.                     pref[j].run_state = threadInfo.run_state;
  528.                     pref[j].flags = threadInfo.flags;
  529.                     pref[j].p_pctcpu = threadInfo.cpu_usage;
  530.                     pref[j].p_cptime = threadInfo.user_time.seconds + 
  531.                                          threadInfo.system_time.seconds;
  532.                     pref[j].cur_priority = threadInfo.cur_priority;
  533.                     pref[j].nthreads = threadCount;
  534.                 } else {
  535.                     pref[j].run_state = 0;
  536.                     pref[j].flags = 0;
  537.                     pref[j].p_pctcpu = 0;
  538.                     pref[j].p_cptime = 0;
  539.                 }
  540.                 /* Get processes memory usage and cputime */
  541.                 if(task_status==KERN_SUCCESS)
  542.                 {
  543.                     pref[j].p_rsize = taskInfo.resident_size/1024;
  544.                     pref[j].p_vsize = taskInfo.virtual_size/1024;
  545.                 } else {
  546.                     pref[j].p_rsize = 0;
  547.                     pref[j].p_vsize = 0;
  548.                 }
  549.                 active_procs++;
  550.                 j++;
  551.             }
  552.         }
  553.         i++;
  554.     } while(pp->p_nxt != 0);
  555.     pref[j].p_self = NULL;  /*  End list of processes with NULL */
  556.  
  557.     /* if requested, sort the "interesting" processes */
  558.      if (compare != NULL)
  559.     {
  560.         qsort((char *)pref, active_procs, sizeof(struct proc_unix), compare);
  561.     }
  562.  
  563.     /* remember active and total counts */
  564.     si->p_total = total_procs;
  565.     si->p_active = pref_count = active_procs;
  566.  
  567.     /* pass back a handle */
  568.     handle.list = pref;
  569.     handle.count = active_procs;
  570.     handle.current = 0;
  571.     return((caddr_t)&handle);
  572. }
  573.  
  574. char fmt[MAX_COLS];        /* static area where result is built */
  575.  
  576. char *format_next_process(caddr_t handle, char *(*get_userid)())
  577. {
  578.     register struct proc *pp;
  579.     register long cputime;
  580.     register double pct, wcpu, pctmem;
  581.     int where;
  582.     struct user u;
  583.     struct handle *hp;
  584.     register int p_pctcpu;
  585.     register int rm_size;
  586.     register int vm_size;
  587.     register int run_state;
  588.     register int flags;
  589.     register int nthreads;
  590.     register int cur_priority;
  591.     char state_str[10];
  592.  
  593.     /* find and remember the next proc structure */
  594.     hp = (struct handle *)handle;
  595.     pp = hp->list[hp->current].p_self;
  596.     p_pctcpu = hp->list[hp->current].p_pctcpu;
  597.     cputime = hp->list[hp->current].p_cptime;
  598.     rm_size = hp->list[hp->current].p_rsize;
  599.     vm_size = hp->list[hp->current].p_vsize;
  600.     run_state = hp->list[hp->current].run_state;
  601.     flags = hp->list[hp->current].flags;
  602.     nthreads = hp->list[hp->current].nthreads;
  603.     cur_priority = hp->list[hp->current].cur_priority;
  604.     hp->current++;
  605.     hp->count--;
  606.  
  607.     /* get the process's user struct and set cputime */
  608.     where = getu(pp, &u);
  609.     if (where == -1)
  610.     {
  611.         (void) strcpy(u.u_comm, "<swapped>");
  612.         cputime = 0;
  613.     }
  614.     else
  615.     {
  616.         /* set u_comm for system processes */
  617.         if (u.u_comm[0] == '\0')
  618.         {
  619.             if (pp->p_pid == 0)
  620.             {
  621.                 (void) strcpy(u.u_comm, "Swapper");
  622.             }
  623.             else if (pp->p_pid == 2)
  624.             {
  625.                 (void) strcpy(u.u_comm, "Pager");
  626.             }
  627.             }
  628.         if (where == 1) {
  629.             /*
  630.              * Print swapped processes as <pname>
  631.              */
  632.             char buf[sizeof(u.u_comm)];
  633.             (void) strncpy(buf, u.u_comm, sizeof(u.u_comm));
  634.             u.u_comm[0] = '<';
  635.             (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2);
  636.             u.u_comm[sizeof(u.u_comm) - 2] = '\0';
  637.             (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1);
  638.             u.u_comm[sizeof(u.u_comm) - 1] = '\0';
  639.         }
  640. /*    User structure does not work.  Use Thread Info to get cputime for process. */
  641. /*        cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec; */
  642.     }
  643.  
  644.  
  645.     /* calculate the base for cpu percentages */
  646.     pct = (double)(p_pctcpu)/TH_USAGE_SCALE;
  647.     wcpu = weighted_cpu(pct, pp);
  648.     pctmem = (double)(rm_size*1024.) / (double)(host_stats.memory_size);
  649.     
  650.     /* Get process state description */
  651.     if(run_state)
  652.     {
  653.         strcpy(state_str, mach_state[run_state]);
  654.         strcat(state_str, flags_state[flags]);
  655.     } else {
  656.         strcpy(state_str, state_abbrev[pp->p_stat]);
  657.     }
  658.     
  659.     /* format this entry */
  660.     sprintf(fmt,
  661.         Proc_format,
  662.         pp->p_pid,
  663.         (*get_userid)(pp->p_uid),
  664.         state_str,
  665.         cur_priority,
  666. /*        pp->p_pri - PZERO, */
  667.         pp->p_nice - NZERO,
  668.         nthreads,
  669.         format_k(vm_size),
  670.         format_k(rm_size),
  671.         100.0 * pctmem,
  672. /*        100.0 * wcpu, */
  673.         100.0 * pct,
  674.         format_time(cputime),
  675.         printable(u.u_comm));
  676.  
  677.     /* return the result */
  678.     return(fmt);
  679. }
  680.  
  681. /*
  682.  *  getu(p, u) - get the user structure for the process whose proc structure
  683.  *    is pointed to by p.  The user structure is put in the buffer pointed
  684.  *    to by u.  Return 0 if successful, -1 on failure (such as the process
  685.  *    being swapped out).
  686.  */
  687.  
  688. getu(register struct proc *p, struct user *u)
  689. {
  690.     register int nbytes, n;
  691.     struct task task;
  692.     struct utask utask;
  693.     struct uthread thread;
  694.  
  695.     /*
  696.      *  Check if the process is currently loaded or swapped out.  The way we
  697.      *  get the u area is totally different for the two cases.  For this
  698.      *  application, we just don't bother if the process is swapped out.
  699.      */
  700.     /* NEXTSTEP proc.h
  701.      * One structure allocated per active
  702.      * process. It contains all data needed
  703.      * about the process while the
  704.      * process may be swapped out.
  705.      * Other per process data (user.h)
  706.      * is swapped with the process.
  707.      */
  708.  
  709.     if ((p->p_flag & SLOAD) == 0) {
  710. /* User info is always in core.
  711.  * #ifdef DOSWAP
  712.  *         if (lseek(swap, (long)dtob(p->p_swaddr), 0) == -1) {
  713.  *             perror("lseek(swap)");
  714.  *             return(-1);
  715.  *         }
  716.  *         if (read(swap, (char *) u, sizeof(struct user)) != sizeof(struct user))  {
  717.  *             perror("read(swap)");
  718.  *             return(-1);
  719.  *         }
  720.  *         return (1);
  721.  * #else
  722.  */
  723.         return(-1);
  724. /*#endif
  725.  */
  726.     }
  727.  
  728.     /*
  729.      *  Process is currently in memory, we hope!
  730.      */
  731.     if(!getkval(p->task, (int *)&task, sizeof(struct task), "task")) {
  732. #ifdef DEBUG
  733.         perror("getkval(p->task)");
  734. #endif
  735.         /* we can't seem to get to it, so pretend it's swapped out */
  736.         return(-1);
  737.     }
  738.  
  739.     if(!getkval(task.u_address, (int *)&utask, sizeof(struct utask), "task.u_address")) {
  740. #ifdef DEBUG
  741.         perror("getkval(task->utask)");
  742. #endif
  743.         /* we can't seem to get to it, so pretend it's swapped out */
  744.         return(-1);
  745.     }
  746.     
  747.     /* Copy utask and uthread info into struct user *u */
  748.     /*  This is incomplete.  Only copied info needed. */
  749.     u->u_procp = utask.uu_procp;
  750.     u->u_ar0 = utask.uu_ar0;
  751.     strcpy(u->u_comm, utask.uu_comm);
  752.     u->u_ru = utask.uu_ru;
  753.     return(0);
  754. }
  755.  
  756. /*
  757.  * check_nlist(nlst) - checks the nlist to see if any symbols were not
  758.  *        found.  For every symbol that was not found, a one-line
  759.  *        message is printed to stderr.  The routine returns the
  760.  *        number of symbols NOT found.
  761.  */
  762.  
  763. int check_nlist(register struct nlist *nlst)
  764. {
  765.     register int i;
  766.  
  767.     /* check to see if we got ALL the symbols we requested */
  768.     /* this will write one line to stderr for every symbol not found */
  769.  
  770.     i = 0;
  771.     while (nlst->n_un.n_name != NULL)
  772.     {
  773.     if (nlst->n_type == 0 && nlst->n_value == 0)
  774.     {
  775.         /* this one wasn't found */
  776.         fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_un.n_name);
  777.         i = 1;
  778.     }
  779.     nlst++;
  780.     }
  781.  
  782.     return(i);
  783. }
  784.  
  785.  
  786. /*
  787.  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
  788.  *    "offset" is the byte offset into the kernel for the desired value,
  789.  *      "ptr" points to a buffer into which the value is retrieved,
  790.  *      "size" is the size of the buffer (and the object to retrieve),
  791.  *      "refstr" is a reference string used when printing error meessages,
  792.  *        if "refstr" starts with a '!', then a failure on read will not
  793.  *          be fatal (this may seem like a silly way to do things, but I
  794.  *          really didn't want the overhead of another argument).
  795.  *      
  796.  */
  797.  
  798. getkval(unsigned long offset, int *ptr, int size, char *refstr)
  799. {
  800.     if (lseek(kmem, (long)offset, L_SET) == -1) {
  801.         if (*refstr == '!')
  802.             refstr++;
  803.         (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, 
  804.                refstr, strerror(errno));
  805.         quit(23);
  806.     }
  807.     if (read(kmem, (char *) ptr, size) == -1) {
  808.         if (*refstr == '!') 
  809.             return(0);
  810.         else {
  811.             (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, 
  812.                refstr, strerror(errno));
  813.             quit(23);
  814.         }
  815.     }
  816.     return(1);
  817. }
  818.     
  819. /* comparison routine for qsort */
  820.  
  821. /*
  822.  *  proc_compare - comparison function for "qsort"
  823.  *    Compares the resource consumption of two processes using five
  824.  *      distinct keys.  The keys (in descending order of importance) are:
  825.  *      percent cpu, cpu ticks, state, resident set size, total virtual
  826.  *      memory usage.  The process states are ordered as follows (from least
  827.  *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
  828.  *      array declaration below maps a process state index into a number
  829.  *      that reflects this ordering.
  830.  */
  831.  
  832. static unsigned char sorted_state[] =
  833. {
  834.     0,    /* not used        */
  835.     3,    /* sleep        */
  836.     1,    /* ABANDONED (WAIT)    */
  837.     6,    /* run            */
  838.     5,    /* start        */
  839.     2,    /* zombie        */
  840.     4    /* stop            */
  841. };
  842.  
  843. proc_compare(struct proc_unix *pp1, struct proc_unix *pp2)
  844. {
  845.     register struct proc *p1 = pp1->p_self;
  846.     register struct proc *p2 = pp2->p_self;
  847.     register int result;
  848.     register pctcpu lresult;
  849.  
  850.     /* compare percent cpu (pctcpu) */
  851.     if ((lresult = pp2->p_pctcpu - pp1->p_pctcpu) == 0)
  852.     {
  853.     /* use cpticks to break the tie */
  854.     if ((result = P_CPTICKS(p2) - P_CPTICKS(p1)) == 0)
  855.     {
  856.         /* use process state to break the tie */
  857.         if ((result = sorted_state[p2->p_stat] - sorted_state[p1->p_stat])  == 0)
  858.         {
  859.         /* use priority to break the tie */
  860.         if ((result = p2->p_pri - p1->p_pri) == 0)
  861.         {
  862.             /* use resident set size (rssize) to break the tie */
  863.             if ((result = pp2->p_rsize - pp1->p_rsize) == 0)
  864.             {
  865.             /* use total memory to break the tie */
  866.             result = pp2->p_vsize - pp1->p_vsize;
  867.             }
  868.         }
  869.         }
  870.     }
  871.     }
  872.     else
  873.     {
  874.     result = lresult < 0 ? -1 : 1;
  875.     }
  876.  
  877.     return(result);
  878. }
  879.  
  880. /*
  881.  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
  882.  *        the process does not exist.
  883.  *        It is EXTREMLY IMPORTANT that this function work correctly.
  884.  *        If top runs setuid root (as in SVR4), then this function
  885.  *        is the only thing that stands in the way of a serious
  886.  *        security problem.  It validates requests for the "kill"
  887.  *        and "renice" commands.
  888.  */
  889.  
  890. int proc_owner(int pid)
  891. {
  892.     register int cnt;
  893.     register struct proc *pp;
  894.  
  895.     cnt = pref_count;
  896.     while (--cnt >= 0)
  897.     {
  898.         pp = pref[cnt].p_self;
  899.         if( pp->p_pid == pid )     /* Modified (pid_t)pid to pid, compiler error. */
  900.         {
  901.             return((int)pp->p_uid);
  902.         }
  903.     }
  904.     return(-1);
  905. }
  906.  
  907. int thread_stats(int pid, struct thread_basic_info *info, int *thread_count)
  908. {
  909.     int                       i;
  910.     kern_return_t             status;
  911.     kern_return_t              status_dealloc;
  912.     task_t                      p_task;
  913.     thread_array_t              thread_list, list;
  914.     struct thread_basic_info  threadInfo;
  915.     unsigned int              info_count = THREAD_BASIC_INFO_COUNT;
  916.  
  917.     /* Get the task pointer for the process. */
  918.     status = task_by_unix_pid( task_self(), pid, &p_task);
  919.     if (status!=KERN_SUCCESS)
  920.     {
  921.         printf("pid = %i\n", pid);
  922.         mach_error("Error calling task_by_unix_pid()", status);
  923.         return status;
  924.     }
  925.     
  926.     /* Get the list of threads for the task. */
  927.     status = task_threads(p_task, &thread_list, thread_count);
  928.     if (status!=KERN_SUCCESS)
  929.     {
  930.         mach_error("Error calling task_threads()", status);
  931.         return status;
  932.     }
  933.  
  934.     /* Get the pctcpu value for each thread and sum the values */
  935.     info->user_time.seconds = 0;
  936.     info->user_time.microseconds = 0;
  937.     info->system_time.seconds = 0;
  938.     info->system_time.microseconds = 0;
  939.     info->cpu_usage = 0;
  940.     info->sleep_time = 0;
  941.  
  942.     for(i=0; i<*thread_count; i++)
  943.     {
  944.         status = thread_info(thread_list[i], THREAD_BASIC_INFO, 
  945.                         (thread_info_t)&threadInfo, &info_count);
  946.         if (status!=KERN_SUCCESS)
  947.         {
  948.             mach_error("Error calling thread_info()", status);
  949.             break; 
  950.         } else {
  951.             if(i==0)
  952.             {
  953.                 info->base_priority = threadInfo.base_priority;
  954.                 info->cur_priority = threadInfo.cur_priority;
  955.                 info->run_state = threadInfo.run_state;
  956.                 info->flags = threadInfo.flags;
  957.                 info->suspend_count = threadInfo.suspend_count;
  958.                 info->sleep_time += threadInfo.sleep_time;
  959.             }
  960.             info->user_time.seconds += threadInfo.user_time.seconds;
  961.             info->user_time.microseconds += threadInfo.user_time.microseconds;
  962.             info->system_time.seconds += threadInfo.system_time.seconds;
  963.             info->system_time.microseconds += threadInfo.system_time.microseconds;
  964.             info->cpu_usage += threadInfo.cpu_usage;
  965.         }
  966.     }
  967.  
  968.     /* Deallocate the list of threads. */
  969.     status_dealloc = vm_deallocate(task_self(), (vm_address_t)thread_list,
  970.                            sizeof(thread_list)*(*thread_count));
  971.     if (status_dealloc != KERN_SUCCESS)
  972.     {
  973.         mach_error("Trouble freeing thread_list", status_dealloc);
  974.         status = status_dealloc;
  975.     }
  976.     return status;
  977. }
  978.  
  979. int mach_load_avg(void)
  980. {
  981.     kern_return_t                    status;
  982.     host_t                           host;
  983.     unsigned int                     info_count;
  984.     struct processor_set_basic_info  info;
  985.     processor_set_t                  default_set;
  986.  
  987.     status=processor_set_default(host_self(), &default_set);
  988.     if (status!=KERN_SUCCESS){
  989.         mach_error("Error calling processor_set_default", status);
  990.         exit(1);
  991.     }
  992.  
  993.     info_count=PROCESSOR_SET_BASIC_INFO_COUNT;
  994.     status=processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO,
  995.                                &host, (processor_set_info_t)&info, &info_count);
  996.     if (status != KERN_SUCCESS)
  997.         mach_error("Error calling processor_set_info", status);
  998.  
  999.     return info.load_average;
  1000. }
  1001.  
  1002. kern_return_t task_stats(int pid, struct task_basic_info *info)
  1003. {
  1004.     kern_return_t             status;
  1005.     task_t                      p_task;
  1006.     unsigned int              info_count=TASK_BASIC_INFO_COUNT;
  1007.  
  1008.     /* Get the task pointer for the process. */
  1009.     status = task_by_unix_pid( task_self(), pid, &p_task);
  1010.     if (status!=KERN_SUCCESS) {
  1011.         printf("pid = %i\n", pid);
  1012.         mach_error("Error calling task_by_unix_pid()", status);
  1013.         return(status);
  1014.     }
  1015.  
  1016.     status=task_info(p_task, TASK_BASIC_INFO, (task_info_t)info, &info_count);
  1017.     if (status!=KERN_SUCCESS) {
  1018.         mach_error("Error calling task_info()", status);
  1019.         return(status);
  1020.     }        
  1021.     return(KERN_SUCCESS);
  1022. }
  1023.